package com.weakteam.orderonline.administratorconsole.gwt.client.design;

import com.allen_sauer.gwt.dnd.client.PickupDragController;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.event.dom.client.*;
import com.google.gwt.http.client.*;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.*;
import com.weakteam.orderonline.administratorconsole.gwt.client.domain.Goods;
import com.weakteam.orderonline.administratorconsole.gwt.client.domain.Menu;
import com.weakteam.orderonline.administratorconsole.gwt.client.util.ConsoleLog;
import com.weakteam.orderonline.administratorconsole.gwt.client.util.uploader.drop.DropHandler;
import com.weakteam.orderonline.administratorconsole.gwt.client.util.uploader.file.*;
import com.weakteam.orderonline.administratorconsole.gwt.client.widget.OrderGoodsDraggableBox;
import com.weakteam.orderonline.administratorconsole.gwt.client.widget.OrderGoodsDraggableBoxDropController;

import java.util.*;

/**
 * @author Nail
 */
public class GoodsEditPage extends Composite {

    private static GoodsEditPageUiBinder uiBinder = GWT.create(GoodsEditPageUiBinder.class);

    interface GoodsEditPageUiBinder extends UiBinder<Widget, GoodsEditPage> {
    }
    private static GoodsEditPage instance = null;

    public static GoodsEditPage getInstance() {
        if (instance == null) {
            instance = new GoodsEditPage();
        }
        return instance;
    }

    public GoodsEditPage() {
        initWidget(uiBinder.createAndBindUi(this));
        this.initPage();
    }

    @UiField
    protected AbsolutePanel orderPanel;
    @UiField
    protected VerticalPanel verticalGoodsPanel;
    @UiField
    protected ListBox menuListbox;
    @UiField
    protected Button addOrderButton;
    @UiField
    protected Button saveButton;
    @UiField
    protected Button mDelete;

    // form fields
    @UiField
    protected Label mLabelEnabled;
    @UiField
    protected Button mEnabled;
    @UiField
    protected TextBox mName;
    @UiField
    protected TextBox mPrice;
    @UiField
    protected TextArea mDescription;
    @UiField
    protected Image mIco;
    @UiField
    protected Image mImage;
    @UiField
    protected TextArea mProperties;


    private PickupDragController widgetDragController;
    private List<OrderGoodsDraggableBox> orderGoodsDraggableBox;
    private Map<String, Integer> menuList;

    private void initPage() {

        // init goods menu
        {
            // initialize our widget drag controller
            this.widgetDragController = new PickupDragController(orderPanel, false);
            this.widgetDragController.setBehaviorMultipleSelection(false);

            // initialize a widget drop controller for the current column
            OrderGoodsDraggableBoxDropController widgetDropController = new OrderGoodsDraggableBoxDropController(verticalGoodsPanel);
            this.widgetDragController.registerDropController(widgetDropController);

            this.loadMenuData();
        }

        menuListbox.addChangeHandler(new ChangeHandler() {
            @Override
            public void onChange(ChangeEvent event) {
                int index = GoodsEditPage.this.menuListbox.getSelectedIndex();
                String menuName = GoodsEditPage.this.menuListbox.getItemText(index);
                GoodsEditPage.this.onSelectMenuList(menuName);
            }
        });

        addOrderButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                Goods goods = new Goods();
                goods.setMenuId(GoodsEditPage.this.getCurentMenuId());
                GoodsEditPage.this.saveData(goods);
            }
        });

        saveButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                for (Iterator<OrderGoodsDraggableBox> it = orderGoodsDraggableBox.iterator(); it.hasNext(); ) {
                    OrderGoodsDraggableBox box = it.next();
                    box.getGoods().setOrder(verticalGoodsPanel.getWidgetIndex(box));
                    if (box.isActive()) GoodsEditPage.this.save(box); // save current open box
                    GoodsEditPage.this.saveData();
                }
            }
        });

        mEnabled.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                boolean isEnabled = mLabelEnabled.getText().compareTo("Активно") == 0;
                if (isEnabled) {
                    mLabelEnabled.setText("Не активно");
                    mEnabled.setText("Активировать");
                } else {
                    mLabelEnabled.setText("Активно");
                    mEnabled.setText("Деактивировать");
                }
                OrderGoodsDraggableBox currentBox = GoodsEditPage.this.getCurrentBox();
                currentBox.setEnabled(!isEnabled);
            }
        });

        mDelete.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                int currentId = GoodsEditPage.this.getCurrentBox().getGoods().getId();
                GoodsEditPage.this.deleteData(currentId);
            }
        });

        this.mPrice.addKeyPressHandler(new KeyPressHandler() {
            @Override
            public void onKeyPress(KeyPressEvent event) {
            if(event.getNativeEvent().getKeyCode()!= KeyCodes.KEY_DELETE &&
                    event.getNativeEvent().getKeyCode()!=KeyCodes.KEY_BACKSPACE &&
                    event.getNativeEvent().getKeyCode()!=KeyCodes.KEY_LEFT &&
                    event.getNativeEvent().getKeyCode()!=KeyCodes.KEY_RIGHT){
                String c = event.getCharCode() + "";
                if(RegExp.compile("[^0-9]").test(c)) GoodsEditPage.this.mPrice.cancelKey();
            }
            }
        });

        DropHandler imageDropHandler = new DropHandler(mImage);
        imageDropHandler.addFileEventHandler(new FileEvent.FileEventHandler() {
            @Override
            public void onFiles(FileEvent event) {
                JsArray<File> files = event.getFiles();
                File file = GoodsEditPage.this.checkImageFiles(files);
                GoodsEditPage.this.uploadImageFile(file, FileUtil.IMAGE);
            }
        });

        DropHandler icoDropHandler = new DropHandler(mIco);
        icoDropHandler.addFileEventHandler(new FileEvent.FileEventHandler() {
            @Override
            public void onFiles(FileEvent event) {
                JsArray<File> files = event.getFiles();
                File file = GoodsEditPage.this.checkImageFiles(files);
                GoodsEditPage.this.uploadImageFile(file, FileUtil.ICO);
            }
        });
    }

    private OrderGoodsDraggableBox getCurrentBox() {
        for (Iterator<OrderGoodsDraggableBox> it = orderGoodsDraggableBox.iterator(); it.hasNext();) {
            OrderGoodsDraggableBox box = it.next();
            if (box.isActive()) return box;
        }
        return null;
    }

    private Integer getCurentMenuId() {
        int index = this.menuListbox.getSelectedIndex();
        String menuName = this.menuListbox.getItemText(index);
        if (this.menuList.containsKey(menuName)) {
            return this.menuList.get(menuName);
        } else {
            return null;
        }
    }

    private void onSelectMenuList(String menuName) {
        if (this.menuList.containsKey(menuName)) {
            this.loadData(this.menuList.get(menuName));
        }
    }


    private void addOrderDraggableBox(final Goods goods) {
        final OrderGoodsDraggableBox box = new OrderGoodsDraggableBox();
        box.init(goods, this.orderGoodsDraggableBox);
        box.setEnabled(goods.getEnabled());
        this.verticalGoodsPanel.add(box);

        this.orderGoodsDraggableBox.add(box);

        // make the widget draggable
        widgetDragController.makeDraggable(box);
    }

    public void setActive(OrderGoodsDraggableBox box) {
        Goods goods = box.getGoods();
        if (goods.getEnabled()) {
            mLabelEnabled.setText("Активно");
            mEnabled.setText("Деактивировать");
        } else {
            mLabelEnabled.setText("Не активно");
            mEnabled.setText("Активировать");
        }
        mName.setText(goods.getName());
        mDescription.setText(goods.getDescription());
        mPrice.setText(goods.getPrice().toString());
        mImage.setUrl(FileUtil.getImageURL(goods.getImage()));
        mIco.setUrl(FileUtil.getImageURL(goods.getIco()));
        mProperties.setText(goods.getProperties());
    }

    public void save(OrderGoodsDraggableBox box) {
        Goods goods = box.getGoods();
        goods.setEnabled(mLabelEnabled.getText().compareTo("Активно") == 0);
        goods.setName(mName.getText());
        goods.setPrice(Double.parseDouble(mPrice.getText()));
        goods.setDescription(mDescription.getText());
        goods.setProperties(mProperties.getText());
    }

    public File checkImageFiles(JsArray<File> files) {
        if (files.length() > 1) {
            Window.alert("Можно добавить только один файл");
        }
        File file = files.get(0);
        String fName = file.getFileName();
        int fSize = file.getFileSize();
        ConsoleLog.getInstance().debug("file name: " + fName);
        ConsoleLog.getInstance().debug("file size: " + fSize);
        if (fSize > 300 * 1024) {
            Window.alert("Файл " + fName + " слишком большой (размер файла не должен превышать 100kb)");
            return null;
        } else if (!fName.endsWith(".png") && !fName.endsWith(".jpg")) {
            Window.alert("Файл " + fName + " не является файлом png или jpg");
            return null;
        } else {
            return file;
        }
    }

    public void uploadImageFile(final File file, final int fileNature) {
        String fileName = file.getFileName();
        String url = GWT.getHostPageBaseURL() + "/uploadImage?name=" + fileName;

        FileUploader fileUploader = new FileUploader();
        fileUploader.addUploadHandler(new UploadHandler() {
            @Override
            public void onUploadProgress(Float percentage) {
                ConsoleLog.getInstance().debug("completed: " + percentage);
            }

            @Override
            public void onUploadCompleted(String savedFileName) {
                ConsoleLog.getInstance().debug("uploaded completed new fileName:" + savedFileName);
                if (fileNature == FileUtil.IMAGE) {
                    GoodsEditPage.this.mImage.setUrl(FileUtil.getImageURL(savedFileName));
                    GoodsEditPage.this.getCurrentBox().getGoods().setImage(savedFileName);
                } else if (fileNature == FileUtil.ICO) {
                    GoodsEditPage.this.mIco.setUrl(FileUtil.getImageURL(savedFileName));
                    GoodsEditPage.this.getCurrentBox().getGoods().setIco(savedFileName);
                }
            }
            @Override
            public void onUploadError() {
                ConsoleLog.getInstance().debug("upload error");
            }
        });
        fileUploader.upload(file, url);
    }

    // http request

    // loadData request
    private void loadMenuData() {
        RequestBuilder builder = new RequestBuilder(RequestBuilder.GET,
                GWT.getHostPageBaseURL() + "/getMenuData");
        try {
            builder.sendRequest(null, new RequestCallback() {

                @Override
                public void onError(Request request, Throwable exception) {
                    ConsoleLog.getInstance().error("loadMenuData onError: " + exception);
                }
                @Override
                public void onResponseReceived(Request request, Response response) {
                    if (200 == response.getStatusCode()) {
                        GoodsEditPage.this.loadMenuDataResponse(response.getText());
                    }
                }
            });
        } catch (RequestException exception) {
            ConsoleLog.getInstance().error("loadMenuData catch error: " + exception);
        }
    }

    // loadData response
    private void loadMenuDataResponse(String json) {
        ConsoleLog.getInstance().debug("loadMenuDataResponse " + json);
        JSONArray data = (JSONArray) JSONParser.parseLenient(json);
        int n = data.size();

        this.menuList = new HashMap<>(n);

        if (n > 0) {
            String firstName = null;
            for(int i = 0; i < n; i++) {
                JSONObject obj = data.get(i).isObject();
                Menu menu = new Menu();
                menu.set(obj);
                if (i == 0) firstName = menu.getName();

                GoodsEditPage.this.menuList.put(menu.getName(), menu.getId());
                GoodsEditPage.this.menuListbox.addItem(menu.getName());
            }
            GoodsEditPage.this.onSelectMenuList(firstName);
        }
    }

    // loadData request
    private void loadData(Integer menuId) {
        String url = GWT.getHostPageBaseURL() + "/getGoodsData?menuId=" + menuId;
        RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);
        try {
            builder.sendRequest(null, new RequestCallback() {

                @Override
                public void onError(Request request, Throwable exception) {
                    ConsoleLog.getInstance().error("loadData onError: " + exception);
                }
                @Override
                public void onResponseReceived(Request request, Response response) {
                    if (200 == response.getStatusCode()) {
                        GoodsEditPage.this.loadDataResponse(response.getText());
                    }
                }
            });
        } catch (RequestException exception) {
            ConsoleLog.getInstance().error("loadData catch error: " + exception);
        }
    }

    // loadData response
    private void loadDataResponse(String json) {
        ConsoleLog.getInstance().debug("loadDataResponse " + json);
        JSONArray data = (JSONArray) JSONParser.parseLenient(json);
        int n = data.size();
        //ConsoleLog.getInstance().debug("n=" + n);
        this.verticalGoodsPanel.clear();
        this.orderGoodsDraggableBox = new ArrayList<OrderGoodsDraggableBox>(n);

        if (n > 0) {
            for(int i = 0; i < n; i++) {
                JSONObject obj = data.get(i).isObject();
                final Goods goods = new Goods();
                goods.set(obj);
                this.addOrderDraggableBox(goods);
            }
            this.orderGoodsDraggableBox.get(0).setActive();
        }
    }


    // saveData
    private void saveData(Goods goods) {
        RequestBuilder builder = new RequestBuilder(RequestBuilder.POST,
                GWT.getHostPageBaseURL() + "/saveGoodsDataOne");
        builder.setHeader("Content-type", "application/x-www-form-urlencoded");
        StringBuffer postData = new StringBuffer();
        postData.append(URL.encode("goods")).append("=").append(URL.encode(goods.getJSON().toString()));
        try {
            builder.sendRequest(postData.toString(), new RequestCallback() {

                @Override
                public void onError(Request request, Throwable exception) {
                    ConsoleLog.getInstance().error("saveData onError: " + exception);
                }
                @Override
                public void onResponseReceived(Request request, Response response) {
                    if (200 == response.getStatusCode()) {
                        GoodsEditPage.this.loadData(GoodsEditPage.this.getCurentMenuId());
                    }
                }
            });
        } catch (RequestException exception) {
            ConsoleLog.getInstance().error("saveData catch error: " + exception);
        }
    }

    // saveData
    private void saveData() {
        if (this.orderGoodsDraggableBox != null && this.orderGoodsDraggableBox.size() > 0) {
            RequestBuilder builder = new RequestBuilder(RequestBuilder.POST,
                    GWT.getHostPageBaseURL() + "/saveGoodsData");
            builder.setHeader("Content-type", "application/x-www-form-urlencoded");

            JSONArray data = new JSONArray();
            int i = 0;
            for (Iterator<OrderGoodsDraggableBox> it = orderGoodsDraggableBox.iterator(); it.hasNext(); ) {
                Goods goods = it.next().getGoods();
                data.set(i++, goods.getJSON());
            }

            StringBuffer postData = new StringBuffer().append(URL.encode("data")).append("=").append(URL.encode(data.toString()));
            try {
                Request request = builder.sendRequest(postData.toString(), new RequestCallback() {

                    @Override
                    public void onError(Request request, Throwable exception) {
                        ConsoleLog.getInstance().error("saveData onError: " + exception);
                    }

                    @Override
                    public void onResponseReceived(Request request, Response response) {
                        if (200 == response.getStatusCode()) {
                            GoodsEditPage.this.loadData(GoodsEditPage.this.getCurentMenuId());
                        }
                    }
                });
            } catch (RequestException exception) {
                ConsoleLog.getInstance().error("saveData catch error: " + exception);
            }
        }
    }

    // deleteData
    private void deleteData(Integer id) {
        RequestBuilder builder = new RequestBuilder(RequestBuilder.POST,
                GWT.getHostPageBaseURL() + "/deleteGoodsData");
        builder.setHeader("Content-type", "application/x-www-form-urlencoded");
        StringBuffer postData = new StringBuffer();
        postData.append(URL.encode("id")).append("=").append(URL.encode(id.toString()));
        try {
            builder.sendRequest(postData.toString(), new RequestCallback() {

                @Override
                public void onError(Request request, Throwable exception) {
                    ConsoleLog.getInstance().error("deleteData onError: " + exception);
                }
                @Override
                public void onResponseReceived(Request request, Response response) {
                    if (200 == response.getStatusCode()) {
                        GoodsEditPage.this.loadData(GoodsEditPage.this.getCurentMenuId());
                    }
                }
            });
        } catch (RequestException exception) {
            ConsoleLog.getInstance().error("deleteData catch error: " + exception);
        }
    }
}